﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Threading.Tasks;

namespace ProgNetComponentsDx.Tasks
{
    /// <summary>
    /// BaseTaskCollection
    /// </summary>
    public class BaseTaskCollection : ICollection<BaseTask>
    {
        /// <summary>
        /// list of tasks
        /// </summary>
        List<BaseTask> list = new List<BaseTask>();
        /// <summary>
        /// Gets or sets a value indicating whether tasks will be performed parallel.
        /// </summary>
        /// <value>
        ///   <c>true</c> if [parallel]; otherwise, <c>false</c>.
        /// </value>
        public bool Parallel { get; set; }
        /// <summary>
        /// Determines whether this instance is running.
        /// </summary>
        /// <returns></returns>
        public bool IsRunning()
        {
            return Running;
        }
        /// <summary>
        /// Occurs when all tasks are finished.
        /// </summary>
        public event EventHandler RunFinished;
        /// <summary>
        /// Raises RunFinished.
        /// </summary>
        private void RaiseRunFinished()
        {
            if (RunFinished != null)
                RunFinished(this, EventArgs.Empty);
        }
        /// <summary>
        /// Checks if every task can run.
        /// </summary>
        /// <returns></returns>
        public bool CanRunTasks()
        {
            foreach (BaseTask t in list)
            {
                if (!t.CanRunTask())
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// Gets a value indicating whether [is canceling].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [is canceling]; otherwise, <c>false</c>.
        /// </value>
        public bool IsCanceling { get; private set; }
        /// <summary>
        /// Cancels this instance.
        /// </summary>
        public void Cancel()
        {
            IsCanceling = true;
            foreach (BaseTask t in list)
            {
                t.Cancel();
            }
        }

        /// <summary>
        /// finishedTasks
        /// </summary>
        private int finishedTasks;
        /// <summary>
        /// Running
        /// </summary>
        private bool Running;

        /// <summary>
        /// Runs all tasks.
        /// </summary>
        public void Run()
        {
            if (CanRunTasks())
            {
                IsCanceling = false;
                Running = true;
                finishedTasks = 0;
                foreach (BaseTask t in list)
                {
                    t.ResetStatus();
                    t.TaskFinished += new EventHandler(t_TaskFinished);
                    t.Scheduler = TaskScheduler.FromCurrentSynchronizationContext();
                }
                if (Parallel)
                {
                    RunTasks(true);
                }
                else
                {
                    Task.Factory.StartNew(RunTasks);
                }
                Task wait = Task.Factory.StartNew(CheckEnd);
                wait.ContinueWith(OnRunFinished, TaskScheduler.FromCurrentSynchronizationContext());
            }
        }

        /// <summary>
        /// Handles the TaskFinished event of the task.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        void t_TaskFinished(object sender, EventArgs e)
        {
            finishedTasks++;
        }

        /// <summary>
        /// Called when [run finished].
        /// </summary>
        /// <param name="task">The task.</param>
        private void OnRunFinished(Task task)
        {
            IsCanceling = false;
            foreach (BaseTask t in list)
            {
                t.TaskFinished -= new EventHandler(t_TaskFinished);
                t.Scheduler = null;
            }
            Running = false;
            if (RunFinished != null)
                RunFinished(this, EventArgs.Empty);
        }
        /// <summary>
        /// Checks the end.
        /// </summary>
        private void CheckEnd()
        {
            while (finishedTasks < Count)
                System.Threading.Thread.Sleep(5);
        }
        /// <summary>
        /// Runs the tasks.
        /// </summary>
        private void RunTasks()
        {
            RunTasks(false);
        }
        /// <summary>
        /// Runs the tasks.
        /// </summary>
        /// <param name="inSeparateTasks">if set to <c>true</c> [in separate tasks].</param>
        private void RunTasks(bool inSeparateTasks)
        {
            foreach (BaseTask t in list)
            {
                if (IsCanceling)
                {
                    finishedTasks = Count;
                    break;
                }
                t.Run(inSeparateTasks);
            }
        }
        /// <summary>
        /// Gets the tasks.
        /// </summary>
        /// <param name="status">The status.</param>
        /// <returns></returns>
        public BaseTask[] GetTasks(TaskStatus status)
        {
            List<BaseTask> l = new List<BaseTask>();
            foreach (BaseTask t in list)
                if (t.Status == status)
                    l.Add(t);
            return l.ToArray();
        }
        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        public void Add(BaseTask item)
        {
            if (!IsRunning())
                list.Add(item);
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        public void Clear()
        {
            if (!IsRunning())
                list.Clear();
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        /// <returns>
        /// true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.
        /// </returns>
        public bool Contains(BaseTask item)
        {
            return list.Contains(item);
        }

        /// <summary>
        ///  Copies the elements of the System.Collections.Generic.ICollection<T> to an System.Array, starting at a particular System.Array index.
        /// </summary>
        /// <param name="array">The array.</param>
        /// <param name="arrayIndex">Index of the array.</param>
        public void CopyTo(BaseTask[] array, int arrayIndex)
        {
            list.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.</returns>
        public int Count
        {
            get { return list.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
        /// </summary>
        /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.</returns>
        public bool IsReadOnly
        {
            get { return IsRunning(); }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        /// <returns>
        /// true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </returns>
        public bool Remove(BaseTask item)
        {
            if (!IsRunning())
                return list.Remove(item);
            return false;
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<BaseTask> GetEnumerator()
        {
            return list.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        /// </returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return list.GetEnumerator();
        }

        /// <summary>
        /// Returns index of task.
        /// </summary>
        /// <param name="task">The task.</param>
        /// <returns></returns>
        public int IndexOf(BaseTask task)
        {
            return list.IndexOf(task);
        }
    }
}
